package com.hotel.management.aspect;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.util.Arrays;

/**
 * 日志切面类
 * 用于记录Service层方法的执行日志和性能监控
 * 
 * @author Hotel Management Team
 * @version 1.0.0
 * @since 2024-06-28
 */
@Aspect
@Component
public class LoggingAspect {

    private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);

    /**
     * 定义Service层的切点
     * 匹配com.hotel.management.service包及其子包下的所有public方法
     */
    @Pointcut("execution(public * com.hotel.management.service..*.*(..))")
    public void serviceLayer() {
    }

    /**
     * 定义Repository层的切点
     * 匹配com.hotel.management.mapper包及其子包下的所有public方法
     */
    @Pointcut("execution(public * com.hotel.management.mapper..*.*(..))")
    public void repositoryLayer() {
    }

    /**
     * 前置通知：在方法执行前记录日志
     * 
     * @param joinPoint 连接点
     */
    @Before("serviceLayer()")
    public void logBefore(JoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        log.info("==> 开始执行: {}.{}(), 参数: {}", 
                className, methodName, Arrays.toString(args));
    }

    /**
     * 后置通知：在方法正常返回后记录日志
     * 
     * @param joinPoint 连接点
     * @param result 返回结果
     */
    @AfterReturning(pointcut = "serviceLayer()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        
        log.info("<== 执行成功: {}.{}(), 返回值: {}", 
                className, methodName, result);
    }

    /**
     * 异常通知：在方法抛出异常后记录日志
     * 
     * @param joinPoint 连接点
     * @param exception 异常信息
     */
    @AfterThrowing(pointcut = "serviceLayer()", throwing = "exception")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable exception) {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        
        log.error("<!> 执行异常: {}.{}(), 异常信息: {}", 
                className, methodName, exception.getMessage(), exception);
    }

    /**
     * 环绕通知：记录方法执行时间
     * 
     * @param proceedingJoinPoint 处理连接点
     * @return 方法执行结果
     * @throws Throwable 可能抛出的异常
     */
    @Around("serviceLayer()")
    public Object logExecutionTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String className = proceedingJoinPoint.getTarget().getClass().getSimpleName();
        String methodName = proceedingJoinPoint.getSignature().getName();
        
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        
        try {
            Object result = proceedingJoinPoint.proceed();
            stopWatch.stop();
            
            long executionTime = stopWatch.getTotalTimeMillis();
            log.info("⏱️ 执行耗时: {}.{}() - {}ms", 
                    className, methodName, executionTime);
            
            // 如果执行时间超过1秒，记录警告日志
            if (executionTime > 1000) {
                log.warn("⚠️ 慢查询警告: {}.{}() 执行时间过长: {}ms", 
                        className, methodName, executionTime);
            }
            
            return result;
        } catch (Throwable throwable) {
            stopWatch.stop();
            long executionTime = stopWatch.getTotalTimeMillis();
            log.error("💥 执行失败: {}.{}() - {}ms, 异常: {}", 
                    className, methodName, executionTime, throwable.getMessage());
            throw throwable;
        }
    }

    /**
     * 数据库操作日志记录
     * 
     * @param joinPoint 连接点
     */
    @Before("repositoryLayer()")
    public void logDatabaseOperation(JoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        log.debug("🗄️ 数据库操作: {}.{}(), 参数: {}", 
                className, methodName, Arrays.toString(args));
    }
}
